package stringFunctions;

import java.util.ArrayList;
import java.util.Arrays;

import processor.ProcessorSuperClass;

import static list.functions.ListFunctions.*;

/**
 * A class containing common functions used in natural language processing.
 * 
 * Fully Tested: No
 * Fully Documented: No
 * Created: 8-Apr-2012
 * Last Updated: 8-Apr-2012
 * 
 * @author Shimu
 */
public class LanguageProcessor extends ProcessorSuperClass {
    
    public LanguageProcessor() {
    }

    /**
     * Returns the substring of str between the given start and end.
     *
     * @param str
     * @param start
     * @param end
     * @return
     */
    public ArrayList<String> substringBetween(String str, 
            String start, String end) {

        ArrayList<String> returnStr = new ArrayList<String>();
        int startTagIndex, endTagIndex, endIndex;
        String subString;

        for (int i = 0; i < str.length(); i++) {
            startTagIndex = str.indexOf(start, i);
            if (startTagIndex == -1) {
                break;
            }

            endIndex = str.indexOf('>', startTagIndex);
            if ((endIndex == -1) || (endIndex == str.length() - 1)) {
                break;
            }

            endTagIndex = str.indexOf(end, endIndex);
            if (endTagIndex == -1) {
                break;
            }

            subString = str.substring(endIndex + 1, endTagIndex).trim();
            if ((!subString.isEmpty()) && (!subString.contains("<"))) {
                returnStr.add(subString);
            }

            i = endTagIndex;
        }

        return returnStr;
    }

    /**
     * Returns a String[] containing the Strings in the given ArrayList<String>
     * ordering preserved.
     *
     * @param strList
     * @return a String[] containing the Strings in the given ArrayList<String>
     */
    public String[] toArray(ArrayList<String> strList) {
        String[] array = new String[strList.size()];
        strList.toArray(array);
        return array;
    }

    /**
     * Sorts the given String[] alphabetically. In place sorting.
     *
     * @param strList
     *            the String[] to be sorted.
     */
    public void sort(String[] strList) {
        
        // Case insensitive sorting
        Arrays.sort(strList, String.CASE_INSENSITIVE_ORDER);
    }

    /**
     * Returns a String[] containing the content of the given ArrayList<String>
     * sorted in alphabetical order.
     *
     * @param strList
     * @return a String[] containing the content of the given ArrayList<String>
     *         in alphabetical order.
     */
    public String[] sort(ArrayList<String> strList) {
        String[] sortedList = toArray(strList);
        sort(sortedList);
        return sortedList;
    }

    /*
     * Given an ArrayList<String> remove all Strings except those that contain
     * the given includeStr as a substring. In-place removal, a new
     * ArrayList<String> is NOT returned.
     *
     * public void include(ArrayList<String> strList, String includeStr) {
     * int i = 0;
     *
     * while (i < strList.size()) {
     * if (!strList.get(i).contains(includeStr)) {
     * strList.remove(i);
     * } else {
     * i++;
     * }
     * }
     * }
     */

    /*
     * Given a strList, remove all elements from strList, unless the element is
     * an element of
     * includeStrs. In place removal, a new ArrayList<String> is NOT returned.
     *
     * public void include(ArrayList<String> strList, String[] includeStrs) {
     *
     * if (includeStrs.length == 0) {
     * return;
     * }
     *
     * int i = 0;
     *
     * while (i < strList.size()) {
     * if (!this.contains(strList.get(i), includeStrs)) {
     * strList.remove(i);
     * } else {
     * i++;
     * }
     * }
     * }
     */
    /**
     * Given a strList, remove all elements from strList, unless the element is
     * an element of includeStrs. In place removal, a new ArrayList<String> 
     * is NOT returned.
     * 
     * @param strList
     * @param includeStrs  
     */
    public void include(ArrayList<String> strList, 
            ArrayList<String> includeStrs) {

        if (includeStrs.isEmpty()) {
            return;
        }

        int i = 0;

        while (i < strList.size()) {
            if (!elementOf(strList.get(i), includeStrs)) {
                strList.remove(i);
            } else {
                i++;
            }
        }
    }

    /**
     * Given an ArrayList<String> remove all Strings that contain a String from
     * excludeStrs. In-place removal, a new ArrayList<String> is
     * NOT returned.
     */
    public void exclude(ArrayList<String> strList, 
            ArrayList<String> excludeStrs) {

        if (excludeStrs.isEmpty()) {
            return;
        }

        int i = 0;

        while (i < strList.size()) {
            if (elementOf(strList.get(i), excludeStrs)) {
                strList.remove(i);
            } else {
                i++;
            }
        }
    }

    /**
     * Convert an ArrayList<String> into a String. Each element of the
     * ArrayList<String> is concatenated together with a newline character (\n)
     * between each element.
     */
    public String convertToString(ArrayList<String> strList) {
        String str = "";

        for (String line : strList) {
            str += line + "\n";
        }

        return str;
    }

    /**
     * Given a String, split the String at the newline character (\n) and append
     * the elements to an ArrayList<String>, which is returned.
     */
    public ArrayList<String> toArrayList(String str) {
        String[] strArray = str.split("\\r?\\n");
        ArrayList<String> strList = new ArrayList<String>();
        strList.addAll(Arrays.asList(strArray));

        return strList;
    }

    /**
     * Returns a new ArrayList<String> containing the elements of the given
     * String[].
     * 
     * @param strArray 
     * @return 
     * @deprecated use Arrays.asList(array) instead
     */
    public ArrayList<String> toArrayList(String[] strArray) {
        ArrayList<String> strList = new ArrayList<String>();
        strList.addAll(Arrays.asList(strArray));
        return strList;
    }

    /**
     * Given a strList, append the capitalized version of each element in
     * strList to strList. Useful if one has a list of words and do not want
     * to type out its capitalized version as well.
     * 
     * @param strList 
     */
    public void appendCapital(ArrayList<String> strList) {
        int size = strList.size();
        String element, capitalized;

        for (int i = 0; i < size; i++) {
            element = strList.get(i);
            if (!element.isEmpty()) {
                capitalized = element.substring(0, 1).toUpperCase()
                        + element.substring(1);
                strList.add(capitalized);
            }

        }
    }

    /**
     * Given an ArrayList<String> strList, replace all the Strings in strList
     * that exist in oldStr with the corresponding String in newStr.
     * Example:
     * strList = ["hello", "Jane&#39s socks"]
     * oldStr = ["ll", "&#39"]
     * newStr = ["w", "'"]
     * after this method runs:
     * strList = ["hewo", "Jane's socks"]
     *
     * The size of oldStr and newStr have to be EXACTLY the same.
     * If the length of oldStr and newStr are not the same, the method exits.
     *
     * @param strList
     * @param oldStr
     * @param newStr
     */
    public void replace(ArrayList<String> strList, String[] oldStr,
            String[] newStr) {

        if (oldStr.length != newStr.length) {
            logger.severe("Replace oldStr different size from replace newStr!");
            return;
        }

        for (int i = 0; i < strList.size(); i++) {
            for (int j = 0; j < oldStr.length; j++) {
                strList.set(i, strList.get(i).replace(oldStr[j], newStr[j]));
            }

        }
    }

    /** 
     * Given an ArrayList<String> returns a new ArrayList<String> 
     * containing the exact same elements. The returned ArrayList<String> 
     * can be modified without affecting the original ArrayList<String>.
     * 
     * @param a_list the ArrayList to be copied
     * @return a copy of the given list
     */
    public ArrayList<String> copyArrayList(ArrayList<String> a_list) {
        ArrayList<String> copy = new ArrayList<String>();
        
        for (String element : a_list) {
            copy.add(element);
        }

        return copy;
    }

    /** 
     * Append all the elements of the given strList to the given arrayList.
     * 
     * @param arrayList
     * @param strList  
     */
    public void addAll(ArrayList<String> arrayList, String[] strList) {
        arrayList.addAll(Arrays.asList(strList));
    }
}
